package com.carousel.test;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewConfigurationCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.ImageView;

import java.util.List;

public class CarouselLayout extends ViewGroup {
    private static final String TAG = "CarouseLayout";

    /**
     * 如果移动角度达到该值，则屏蔽点击
     */
    private static final int NOCLICK_VALUE = 3;

    /**
     * 当每秒移动角度达到该值时，认为是快速移动
     */
    private static final int FLINGABLE_VALUE = 100;

    /**
     * View的缩放临界值
     */
    private static final float SCALE = 0.6f;

    private int itemSize;

    /**
     * 直径
     */
    private int mRadius;
    private float mRadiusRate = 0.8f;
    /**
     * 记录上一次的x，y坐标
     */
    private float mLastX;
    private float mLastY;

    /**
     * 检测按下到抬起时旋转的角度
     */
    private double mTmpAngle;
    /**
     * 检测按下到抬起时使用的时间
     */
    private long mDownTime;

    /**
     * 判断是否正在自动滚动
     */
    private boolean isFling;
    /**
     * 布局时的开始角度
     */
    private double mStartAngle = 90d;

    /**
     * 当每秒移动角度达到该值时，认为是快速移动
     */
    private int mFlingableValue = FLINGABLE_VALUE;
    /**
     * 自动滚动的Runnable
     */
    private AutoFlingRunnable mFlingRunnable;

    /**
     * 自动滚动的参数
     */
    private long delayTime = 3000;
    private boolean isAutoScroll = false;
    private static final int AUTO_MESSAGE = 0x01;
    private static final int AUTO_PAUSE = 0x02;

    private int mTouchSlop;

    public static void LOG(String msg) {
        //Log.e(TAG, msg);
    }

    public CarouselLayout(Context context) {
        super(context);
        init();
    }

    public CarouselLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CarouselLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);

        final float density = getContext().getResources().getDisplayMetrics().density;
        itemSize = (int) (density * 78f);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 获得它的父容器为它设置的测量模式和大小
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

        LOG("onMeasure" + sizeWidth + "," + sizeHeight);

        // 如果是warp_content情况下，记录宽和高
        int width = 0;
        int height = 0;
        /**
         * 记录每一行的宽度，width不断取最大宽度
         */
        int lineWidth = 0;

        int cCount = getChildCount();
        // 遍历每个子元素
        for (int i = 0; i < cCount; i++) {
            View child = getChildAt(i);
            // 测量每一个child的宽和高
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            // 当前子控件实际占据的宽度
            int childWidth = child.getMeasuredWidth();
            // 当前子空间实际占据的高度
            int childHeight = child.getMeasuredHeight();
            /**
             * 如果加入当前child，则超出最大宽度，则的到目前最大宽度给width，类加height 然后开启新行
             */
            if (lineWidth + childWidth > sizeWidth) {
                width = Math.max(lineWidth, childWidth);// 取最大的
                lineWidth = childWidth; // 重新开启新行，开始记录
            } else {
                // 否则累加值lineWidth,lineHeight取最大高度
                lineWidth += childWidth;
            }
            // 如果是最后一个，则将当前记录的最大宽度和当前lineWidth做比较
            if (i == cCount - 1) {
                width = Math.max(width, lineWidth);
            }
            height = Math.max(height, childHeight);
        }

        height = height + getPaddingTop() + getPaddingBottom();
        setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth
                : width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight
                : height);

    }

    /**
     * 设置ChildView的alpha值和缩放值
     *
     * @param child
     * @param startAngle
     * @param angleDelay
     */
    private void setChildAlphaAndScale(View child, double startAngle, float angleDelay) {
        if (startAngle >= 80 && startAngle <= 100) {
            ViewCompat.setAlpha(child, 1.0f);
            setScale(child, 1.0f);
        } else {
            ViewCompat.setAlpha(child, SCALE);
            float scale = SCALE;
            if (startAngle > 100 && startAngle < 270) {
                float f = (float) (startAngle - 100) / 170f;
                scale = 1.0f - (1.0f - SCALE) * f;
            } else if (startAngle > 270 || startAngle < 80) {
                double tempAngle = startAngle;
                if (startAngle >= 0 && startAngle < 80) {
                    tempAngle = 360 + startAngle;
                }
                float f = (float) (tempAngle - 270) / 170f;
                scale = scale + (1.0f - SCALE) * f;
            }
            if (scale < SCALE) {
                scale = SCALE;
            }
            if (scale > 1.0f) {
                scale = 1.0f;
            }
            //LOG(scale + "");
            setScale(child, scale);
        }
    }

    public void setScale(View view, float scale) {
        ViewCompat.setScaleX(view, scale);
        ViewCompat.setScaleY(view, scale);
    }

    /**
     * 设置直径控件宽度的百分比（0.5-1.0）
     *
     * @param radius
     */
    public void setRadius(float radius) {
        if (radius >= 0.5f && radius < 1.0f) {
            mRadiusRate = radius;
            requestLayout();
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        int count = getChildCount();

        mRadius = (int) (width * mRadiusRate);

        float angleDelay = 360 / count;
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == View.GONE) {
                continue;
            }

            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            mStartAngle %= 360;
            // 计算，中心点到menu item中心的距离
            float tmp = (mRadius - childWidth) / 2f;
            int left = width / 2 + (int) Math.round(tmp
                    * Math.cos(Math.toRadians(mStartAngle)) - 1 / 2f
                    * childWidth);
            int right = left + childWidth;
            int top = (height - childHeight) / 2;
            int bottom = top + childHeight;
            setChildAlphaAndScale(child, mStartAngle, angleDelay);
            child.setTag(mStartAngle);
            child.layout(left, top, right, bottom);

            //LOG("i=" + i + ",tag=" + child.getTag() + ",angle=" + mStartAngle);
            mStartAngle += angleDelay;
        }
    }

    /**
     * 根据Child0的当前角度计算目标角度
     */
    public synchronized void computeChildPosition() {
        if (isFling) {
            return;
        }
        int count = getChildCount();
        if (count <= 0) {
            return;
        }

        double LIMIT = 60d;
        double angleDelay = 360 / count;
        double angle = Double.parseDouble(String.valueOf(getChildAt(0).getTag()));

        //child0的目标角度
        double toAngle = 0;
        if (angle >= 0 && angle <= 90d) {
            //第一象限
            if (angle >= (90d - LIMIT)) {
                toAngle = 90d;
            } else {
                toAngle = 330d;
            }
        } else if (angle > 90d && angle <= 180d) {
            //第二象限
            if (angle > (90 + LIMIT)) {
                toAngle = 90d + angleDelay;
            } else {
                toAngle = 90d;
            }
        } else if (angle > 180d && angle <= 270d) {
            //第三象限
            double an1 = 90d + angleDelay + LIMIT;
            if (angle > an1) {
                toAngle = 90 + 2 * angleDelay;
            } else {
                toAngle = 90d + angleDelay;
            }
        } else if (angle > 270d && angle < 360d) {
            //第四象限
            double an1 = 90d + 2 * angleDelay + LIMIT;
            if (an1 >= 360d && (an1 % 360d > (90 - LIMIT))) {
                toAngle = 90d;
            } else {
                toAngle = 330d;
            }
        }
        LOG("child0:" + angle + "," + mStartAngle + "----->" + toAngle);
        post(mFlingRunnable = new AutoFlingRunnable(toAngle));
    }

    /**
     * 自动滚动
     */
    public synchronized void computeChildAutoScroll() {
        if (isFling) {
            return;
        }
        int count = getChildCount();
        if (count <= 0) {
            return;
        }

        double angle = Double.parseDouble(String.valueOf(getChildAt(0).getTag()));
        double angleDelay = 360 / count;
        LOG("computeChildAutoScroll:" + angle);
        post(mFlingRunnable = new AutoFlingRunnable(angle + angleDelay));
    }

    private void requestParentDisallowInterceptTouchEvent(boolean disallowIntercept) {
        final ViewParent parent = getParent();
        if (parent != null) {
            parent.requestDisallowInterceptTouchEvent(disallowIntercept);
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                mDownTime = System.currentTimeMillis();
                mTmpAngle = 0;
                requestParentDisallowInterceptTouchEvent(true);
                if (isAutoScroll) {
                    pause();
                }
                // 如果当前已经在快速滚动
                if (isFling) {
                    // 移除快速滚动的回调
                    pause();
                    removeCallbacks(mFlingRunnable);
                    isFling = false;
                    return true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                final float xDiff = Math.abs(mLastX - x);
                final float yDiff = Math.abs(mLastY - y);
                if(yDiff > mTouchSlop){
                    requestParentDisallowInterceptTouchEvent(false);
                    return true;
                }
                /**
                 * 获得开始的角度
                 */
                float start = getAngle(mLastX, mLastY);
                /**
                 * 获得当前的角度
                 */
                float end = getAngle(x, y);
                int quadrant = getQuadrant(x, y);
                if (quadrant == 1 || quadrant == 4) {
                    // 如果是一、四象限，则直接end-start，角度值都是正值
                    mStartAngle += end - start;
                    mTmpAngle += end - start;
                } else {
                    // 二、三象限，色角度值是付值
                    mStartAngle += start - end;
                    mTmpAngle += start - end;
                }
                // 重新布局
                requestLayout();
                mLastX = x;
                mLastY = y;
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                // 计算，每秒移动的角度
                /*float anglePerSecond = ((float) mTmpAngle) * 1000 / (System.currentTimeMillis() - mDownTime);
                // 如果达到该值认为是快速移动
                if (Math.abs(anglePerSecond) > mFlingableValue && !isFling) {
                    // post一个任务，去自动滚动
                    computeChildAutoScroll();
                    return true;
                }*/

                computeChildPosition();

                if (isAutoScroll) {
                    onResume();
                }
                // 如果当前旋转角度超过NOCLICK_VALUE屏蔽点击
                if (Math.abs(mTmpAngle) > NOCLICK_VALUE) {
                    return true;
                }
                break;
        }
        return super.dispatchTouchEvent(event);
    }

    /**
     * 主要为了action_down时，返回true
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return true;
    }

    /**
     * 根据触摸的位置，计算角度
     *
     * @param xTouch
     * @param yTouch
     * @return
     */
    private float getAngle(float xTouch, float yTouch) {
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        double x = xTouch - (width / 2d);
        double y = yTouch - (height / 2d);
        return (float) (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
    }

    /**
     * 根据当前位置计算象限
     *
     * @param x
     * @param y
     * @return
     */
    private int getQuadrant(float x, float y) {
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        int tmpX = (int) (x - width / 2);
        int tmpY = (int) (y - height / 2);
        if (tmpX >= 0) {
            return tmpY >= 0 ? 4 : 1;
        } else {
            return tmpY >= 0 ? 3 : 2;
        }
    }

    /**
     * 自动滚动的任务
     *
     * @author zhy
     */
    private class AutoFlingRunnable implements Runnable {
        private double toAngle;
        private double MOVE_ANGLE = 5d;

        public AutoFlingRunnable(double toAngle) {
            this.toAngle = toAngle;
            if (mStartAngle >= 360d) {
                this.toAngle = toAngle + 360d;
                if (this.toAngle >= 690) {
                    //纠正
                    this.toAngle = toAngle;
                }
            }

            LOG("AutoFlingRunnable:" + this.toAngle);
            if (mStartAngle > this.toAngle) {
                MOVE_ANGLE = MOVE_ANGLE * -1;
            }
        }

        @Override
        public void run() {
            //当前其实角度与目标角度一致
            if (mStartAngle == toAngle) {
                isFling = false;
                return;
            }
            //当前其实角度与目标角度差的绝对值小雨MOVE_ANGLE则赋值为目标角度
            double abs = Math.abs((mStartAngle - toAngle)) % 360;
            if (abs <= Math.abs(MOVE_ANGLE)) {
                removeCallbacks(mFlingRunnable);
                mStartAngle = toAngle;
                requestLayout();
                isFling = false;
                return;
            }

            isFling = true;
            mStartAngle += MOVE_ANGLE;
            postDelayed(this, 30);
            // 重新布局
            requestLayout();
        }
    }

    /**
     * 设置自动滚动
     *
     * @param isAutoScroll
     * @param delayTime    最短1秒
     */
    public void setAutoScroll(boolean isAutoScroll, long delayTime) {
        this.isAutoScroll = isAutoScroll;
        this.delayTime = delayTime < 1000 ? 1000 : delayTime;

        if (!this.isAutoScroll) {
            stop();
        }
    }

    /**
     * 停止滚动
     */
    private void destroy() {
        stop();
        mHandler = null;
    }

    /**
     * 停止
     */
    public void stop() {
        isAutoScroll = false;
        pause();
    }

    /**
     * 暂停
     */
    public void pause() {
        if (null == mHandler) {
            return;
        }
        if (mHandler.hasMessages(AUTO_MESSAGE)) {
            mHandler.removeMessages(AUTO_MESSAGE);
        }
        mHandler.removeCallbacksAndMessages("");
    }

    /**
     * 重新开始
     */
    public void resume() {
        mStartAngle = 90d;
        requestLayout();
        onResume();
    }

    private void onResume() {
        if (null == mHandler || !isAutoScroll) {
            return;
        }

        if (mHandler.hasMessages(AUTO_MESSAGE)) {
            mHandler.removeMessages(AUTO_MESSAGE);
        }
        mHandler.sendEmptyMessageDelayed(AUTO_MESSAGE, delayTime);
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case AUTO_MESSAGE:
                    if (isAutoScroll && getChildCount() > 1) {
                        computeChildAutoScroll();
                        sendEmptyMessageDelayed(AUTO_MESSAGE, delayTime);
                    }
                    break;
                case AUTO_PAUSE:
                    break;
            }
        }
    };

    /**
     * 设置Items
     *
     * @param items
     */
    public void setCarouselItems(List<CarouselItem> items) {
        if (null == items || items.size() <= 0) {
            return;
        }

        this.removeAllViews();

        for (final CarouselItem item : items) {
            final ImageView imageView = new ImageView(getContext());
            //imageView.setScaleType(ImageView.ScaleType.FIT_XY);
            imageView.setImageResource(item.getDrawableRes());
            imageView.setClickable(true);
            imageView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (null != onItemClickListener) {
                        onItemClickListener.onItemClick(imageView, item.getTag());
                    }
                }
            });
            LayoutParams layoutParams = new LayoutParams(itemSize, itemSize);
            this.addView(imageView, layoutParams);
        }

        mStartAngle = 90d;
        requestLayout();
    }

    /**
     * 设置Items
     *
     * @param views
     */
    public void setCarouselViews(List<View> views) {
        if (null == views || views.size() <= 0) {
            return;
        }

        this.removeAllViews();

        int i = 0;
        for (final View view : views) {
            final int tag = i;
            view.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (null != onItemClickListener) {
                        onItemClickListener.onItemClick(view, tag);
                    }
                }
            });
            this.addView(view);
            i++;
        }

        mStartAngle = 90d;
        requestLayout();
    }

    private OnItemClickListener onItemClickListener;

    /**
     * 设置点击事件
     *
     * @param onItemClickListener
     */
    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    /**
     * 点击事件接口
     */
    public interface OnItemClickListener {
        void onItemClick(View view, int tag);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        LOG("onDetachedFromWindow");
        if (null == mFlingRunnable) {
            removeCallbacks(mFlingRunnable);
        }
        destroy();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        LOG("hasFocus=" + hasFocus);
        if (isAutoScroll) {
            if (hasFocus) {
                resume();
            } else {
                pause();
            }
        }
    }
}
